AGENTS.md - Project Guidelines

General Rules

Tech Stack

UI Components

Data Fetching Pattern

Ikuti pola untuk optimasi navigasi dan caching:

1. Server Component dengan Suspense (page.tsx)

// app/[feature]/page.tsx
import { Suspense } from 'react'
import { headers } from 'next/headers'
import { getFeatureDataAction } from '@/app/actions/feature'
import { FeatureClient } from './feature-client'
import { FeatureSkeleton } from '@/components/skeletons/feature-skeleton'

async function FeatureContent() {
  // Deteksi client-side navigation untuk skip server fetch
  const headersList = await headers()
  const referer = headersList.get('referer') || ''
  const host = headersList.get('host') || ''
  const isClientNavigation = referer.includes(host) && referer.includes('/dashboard')

  // Skip fetch jika client navigation - React Query akan gunakan cache
  let initialData = null
  if (!isClientNavigation) {
    const result = await getFeatureDataAction()
    initialData = result.success && result.data ? result.data : null
  }

  return <FeatureClient initialData={initialData} />
}

export default function FeaturePage() {
  return (
    <Suspense fallback={<FeatureSkeleton />}>
      <FeatureContent />
    </Suspense>
  )
}

2. Server Action untuk Data Fetching

// app/actions/feature.ts
'use server'
import { createClient } from '@/lib/supabase/server'

export async function getFeatureDataAction() {
  try {
    const supabase = await createClient()
    // ... fetch logic
    return { success: true, data }
  } catch (error) {
    return { success: false, error: 'Failed to fetch' }
  }
}

3. React Query Hook untuk Client-Side Caching

// lib/hooks/use-feature-data.ts
'use client'
import { useQuery, useQueryClient } from '@tanstack/react-query'

export const featureKeys = {
  all: ['feature'] as const,
  data: () => [...featureKeys.all, 'data'] as const,
}

export function useFeatureData<T>(initialData?: T) {
  return useQuery({
    queryKey: featureKeys.data(),
    queryFn: async () => {
      const { getFeatureDataAction } = await import('@/app/actions/feature')
      const result = await getFeatureDataAction()
      if (!result.success) throw new Error(result.error)
      return result.data as T
    },
    initialData,
    staleTime: 5 * 60 * 1000, // 5 menit
  })
}

export function useInvalidateFeature() {
  const queryClient = useQueryClient()
  return () => queryClient.invalidateQueries({ queryKey: featureKeys.all })
}

4. Client Component dengan React Query

// app/[feature]/feature-client.tsx
'use client'
import { useFeatureData, useInvalidateFeature } from '@/lib/hooks/use-feature-data'

interface FeatureClientProps {
  initialData: FeatureData | null
}

export function FeatureClient({ initialData }: FeatureClientProps) {
  const { data, isLoading } = useFeatureData(initialData || undefined)
  const invalidate = useInvalidateFeature()

  // Gunakan data dari React Query (dengan fallback ke initialData)
  const featureData = data || initialData

  // Panggil invalidate() setelah mutation
}

5. Mutation dengan Cache Invalidation

// Di client component setelah mutation berhasil
const invalidate = useInvalidateFeature()

const handleSave = async () => {
  const result = await saveFeatureAction(data)
  if (result.success) {
    invalidate() // Invalidate React Query cache
  }
}

PENTING: Aturan Data Fetching

Contoh Implementasi

File Structure

app/
├── actions/          # Server actions
├── api/              # API routes
├── dashboard/        # Protected routes
│   └── [feature]/
│       ├── page.tsx           # Server component
│       └── feature-client.tsx # Client component
lib/
├── db/
│   ├── data-access/  # Data fetching functions + cache tags
│   └── services/     # Database service classes
├── supabase/
│   ├── client.ts     # Browser client
│   ├── server.ts     # Server client
│   └── middleware.ts # Auth middleware
├── stores/           # Zustand stores
└── utils/            # Utility functions
components/
├── ui/               # shadcn/ui components
└── features/         # Feature-specific components

Authentication

Testing

Server Actions Pattern

'use server'

export async function myAction(data: FormData) {
  const supabase = await createClient()
  const { data: { user }, error: authError } = await supabase.auth.getUser()

  if (authError || !user) {
    return { success: false, error: 'Unauthorized' }
  }

  // ... action logic

  revalidateTag(CACHE_TAGS.feature)
  revalidatePath('/dashboard/feature')
  return { success: true, data: result }
}

Form Handling

Environment Variables